package com.witsight.hibernate.dao.impl;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

import org.hibernate.engine.spi.NamedQueryDefinition;
import org.hibernate.query.Query;
import org.hibernate.query.spi.NamedQueryRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.util.Assert;

import com.witsight.hibernate.dao.AliasToBeanNestedResultTransformer;
import com.witsight.hibernate.dao.BaseJpaRepository;
import com.witsight.hibernate.dao.CustomAliasToBeanResultTransformer;
import com.witsight.hibernate.util.SqlBuilder;
import com.witsight.platform.dao.CommonRepository;
import com.witsight.platform.model.BoEntity;
import com.witsight.platform.model.IBaseObject;
import com.witsight.platform.model.SearchBean;
import com.witsight.platform.util.lang.BeanUtil;
import com.witsight.platform.util.lang.MapUtil;

import joinery.DataFrame;

/**   
 * 说明：动态sql/hql语言的实现类
 * @Title: DynamicSimpleJpaRepository.java 
 * @Package com.witsight.hibernate.dao 
 * @See: {@link SimpleJpaRepository}
 *  		 {@link BaseJpaRepository}
 * Copyright: Copyright (c) 2017
 * Company:sany huax witsight team by product
 * @author: fuyp  
 * @date: 2017年9月14日 下午2:35:51 
 * @version: V1.0
 *
 */
public class DynamicSimpleJpaRepository<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseJpaRepository<T, ID> {
	/**
	 * 说明：定义后能再同一类中使用static常量log
	 */
	private static final Logger log = LoggerFactory.getLogger(DynamicSimpleJpaRepository.class);
	// namedQueryRepository 为了取得配置的sql／hql config file (key:vaule)
	private final NamedQueryRepository namedQueryRepository;
	private final JpaEntityInformation<T, ?> entityInformation;
	private final EntityManager em;
	private JpaCommonRepositoryImpl commonRepository;
	/**
	 * 
	 * 说明：构造函数 
	 * @Title:DynamicSimpleJpaRepository.java
	 * @param entityInformation
	 * @param entityManager
	 * @param sessionFactory
	 * @date 2017年9月14日 下午3:14:14
	 */
	public DynamicSimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager, 
			NamedQueryRepository namedQueryRepository, CommonRepository commonRepository) {
		super(entityInformation, entityManager);
		this.entityInformation = entityInformation;
		this.em = entityManager;
		this.namedQueryRepository = namedQueryRepository;
		Assert.isInstanceOf(JpaCommonRepositoryImpl.class, commonRepository, "commonRepository type is not CommonRepositoryImpl");
		this.commonRepository = (JpaCommonRepositoryImpl)commonRepository;
	}
	/**
	 * 说明：根据 namequery的name和参数 取得对应的queryString
	 * 
	 * @Title: getQueryString
	 * @See: {@link #getQueryByNameAndParams(String, BoEntity, boolean)}
	 * @author fuyp
	 * @param queryName  查询的名称
	 * @param params 参数
	 * @return 
	 * @date: 2017年9月14日 下午4:07:31
	 */
	private Query<T> getQueryByNameAndParams(String queryName, IBaseObject params) {
		BoEntity bparam= BeanUtil.object2Maps(params);
		return getQueryByNameAndParams(queryName,bparam,true);
	}
	/**
	 * 说明：根据 namequery的name和参数 取得对应的queryString
	 * 
	 * @Title: getQueryString
	 * @See: {@link #getQueryByNameAndParams(String, BoEntity, boolean)}
	 * @author fuyp
	 * @param queryName  查询的名称
	 * @param params 参数
	 * @param isRetClass 是否返回的是实体对象
	 * @return 
	 * @date: 2017年9月14日 下午4:07:31
	 */
	@SuppressWarnings({ "deprecation", "unchecked" })
	private <S> Query<S> getQueryByNameAndParams(String queryName, BoEntity params, boolean isRetClass) {
		Assert.notNull(namedQueryRepository, "namedQueryRepository is empty.");
		NamedQueryDefinition namedQueryDefinition = namedQueryRepository.getNamedQueryDefinition(queryName);
		Assert.notNull(namedQueryDefinition,"namedQueryDefinition of hql not be found  by" + queryName);		
		String queryString = SqlBuilder.parse(namedQueryDefinition.getQueryString(), params);
		Assert.notNull(queryString,"namedQueryDefinition of hql not be found  by" + queryName);
		// 创建 Query对象
		Query<S> query = null;
		if (isRetClass) {
			if (log.isDebugEnabled()) {
				log.debug("run hql:{} , return:{}. " , queryString, getDomainClass());
			}
			
			query = (Query<S>) this.em.createQuery(queryString, getDomainClass());
		} else {
			if (log.isDebugEnabled()) {
				log.debug("run hql:{} , return:{}. " , queryString, BoEntity.class);
			}
			
			query = (Query<S>) this.em.createQuery(queryString);
			query.setResultTransformer(new AliasToBeanNestedResultTransformer(BoEntity.class));
		}
		
		
		if (MapUtil.isNotEmpty(params)) {
			query.setProperties(params);
		}
		// 设置query的属性
		query.setCacheable(namedQueryDefinition.isCacheable());
		query.setCacheMode(namedQueryDefinition.getCacheMode());
		query.setCacheRegion(namedQueryDefinition.getCacheRegion());
		query.setReadOnly(namedQueryDefinition.isReadOnly());
		query.setLockOptions(namedQueryDefinition.getLockOptions());
		if (namedQueryDefinition.getLockOptions() != null) {
			query.setLockOptions(namedQueryDefinition.getLockOptions());
		}
		if (namedQueryDefinition.getMaxResults() != null) {
			query.setMaxResults(namedQueryDefinition.getMaxResults());
		}
		if (namedQueryDefinition.getTimeout() != null) {
			query.setTimeout(namedQueryDefinition.getTimeout());
		}
		
		return query;
	}
	/**
	 * 
	 * 说明:根据map参数取得所有数据
	 * 
	 * @Title: findAllByQuery
	 * @See: {@link #findAllByParams(String, Map)}
	 * @author fuyp
	 * @param namequery
	 *            namequery中配置的key
	 * @param params
	 *            检索条件map对象
	 * @return 全部数据
	 * @date: 2015年4月22日 下午2:56:13
	 */
	@Override
	public List<T> findAllByParams(String queryName, IBaseObject params) {
		TypedQuery<T> typedQuery = getQueryByNameAndParams(queryName, params);
		return typedQuery.getResultList();
	}
	/**
	 * 
	 * 说明：根据map参数取得所有BoEntity数据 (Hql)
	 * @Title: findBoEntitysByParams
	 * @See: {@link #findBoEntitysByParams(String, BoEntity)}
	 * @author fuyp
	 * @param queryName namequery中配置的key
	 * @param params 检索条件map对象
	 * @return 所有BoEntity数据
	 * @date: 2017年9月18日 下午2:37:28
	 */
	@Override
	public List<BoEntity> findBoEntitysByParams(String queryName, IBaseObject params) {
		BoEntity bparam= BeanUtil.object2Maps(params);
		TypedQuery<BoEntity> typedQuery = getQueryByNameAndParams(queryName, bparam, false);
		return typedQuery.getResultList();
	}
	/**
	 * 
	 * 说明：根据map参数取得所有数据 (SQL)
	 * @Title: findSqlAllByParams
	 * @See: {@link #findSqlAllByParams(String, Map)}
	 * @author fuyp
	 * @param queryName namequery中配置的key
	 * @param params 检索条件map对象
	 * @return 
	 * @date: 2017年9月15日 下午1:45:28
	 */
	@Override
	public <O> List<O> findSqlAllByParams(String queryName, IBaseObject params) {
		return this.commonRepository.findSqlAllByParams(queryName, params);
	}
	
	/**
	 * 
	 * 说明：根据map参数取得一条数据
	 * @Title: findOneByParams
	 * @See: {@link #findOneByParams(String, Map)}
	 * @author fuyp
	 * @param queryName  namequery中配置的key
	 * @param mapObject 检索条件map对象
	 * @return 一条数据
	 * @date: 2017年9月15日 上午10:25:38
	 */
	@Override
	public T findOneByParams(String queryName, IBaseObject params){
		TypedQuery<T> typedQuery = getQueryByNameAndParams(queryName, params);
		typedQuery.setMaxResults(1);
		List<T> list = typedQuery.getResultList();
		return list.size()>0 ? list.get(0) : null;
	}
	/**
	 * 
	 * 说明：根据map参数取得一条数据 (Sql)
	 * @Title: findSqlOneByParams
	 * @See: {@link #findSqlOneByParams(String, Map)}
	 * @author fuyp
	 * @param queryName  namequery中配置的key
	 * @param mapObject 检索条件map对象
	 * @return 一条数据
	 * @date: 2017年9月15日 上午10:25:38
	 */
	@Override
	public <O> O findSqlOneByParams(String queryName, IBaseObject params){
		return this.commonRepository.findSqlOneByParams(queryName, params);
	}
	/**
	 * 
	 * 说明：根据map参数取得一条BoEntity数据 (Hql)
	 * @Title: findBoEntitysByParams
	 * @See: {@link #findBoEntitysByParams(String, BoEntity)}
	 * @author fuyp
	 * @param queryName namequery中配置的key
	 * @param params 检索条件map对象
	 * @return 一条BoEntity数据
	 * @date: 2017年9月18日 下午2:37:28
	 */
	@Override
	public BoEntity findBoEntityByParams(String queryName, IBaseObject params) {
		BoEntity bparam= BeanUtil.object2Maps(params);
		TypedQuery<BoEntity> typedQuery = getQueryByNameAndParams(queryName, bparam,false);
		typedQuery.setMaxResults(1);
		return typedQuery.getSingleResult();
	}
	/**
	 * 
	 * 说明：根据翻页取得所有的对象，通过动态hql
	 * @Title: findPageByParams
	 * @See: {@link #findPageByParams(String, Map, Pageable)}
	 * @author fuyp
	 * @param queryName namequery中配置的key
	 * @param mapObject 检索条件map对象
	 * @param pageable 翻页参数
	 * @return 翻页结果集合
	 * @date: 2017年9月15日 上午10:25:47
	 */
	@Override
	public Page<T> findPageByParams(String queryName, IBaseObject params, Pageable pageable) {
		TypedQuery<T> typedQuery = getQueryByNameAndParams(queryName, params);
		return readPage(typedQuery, getDomainClass(), pageable, (Specification<T>) null);
	}
	/**
	 * 
	 * 说明：根据翻页取得所有的对象，通过动态hql
	 * @Title: findPageByParams
	 * @See: {@link #findPageByParams(String, Map, Pageable)}
	 * @author fuyp
	 * @param queryName namequery中配置的key
	 * @param mapObject 检索条件map对象
	 * @param pageable 翻页参数
	 * @return 翻页结果集合
	 * @date: 2017年9月15日 上午10:25:47
	 */
	@Override
	public Page<BoEntity> findPageBoEntityByParams(String queryName, IBaseObject params, Pageable pageable) {
		BoEntity bparam= BeanUtil.object2Maps(params);
		Query<BoEntity> typedQuery = getQueryByNameAndParams(queryName, bparam,false);
		return this.commonRepository.readSqlPage(typedQuery, bparam, pageable);
	}
	/**
	 * 
	 * 说明：根据翻页取得所有的对象，通过动态sql
	 * @Title: findSqlPageByParams
	 * @See: {@link #findSqlPageByParams(String, Map, Pageable)}
	 * @author fuyp
	 * @param queryName namequery中配置的key
	 * @param mapObject 检索条件map对象
	 * @param pageable 翻页参数
	 * @return 翻页结果集合
	 * @date: 2017年9月15日 上午10:25:47
	 */
	@Override
	public <O> Page<O> findSqlPageByParams(String queryName, IBaseObject params, Pageable pageable) {
		return this.commonRepository.findSqlPageByParams(queryName, params, pageable);
	}
	
	@Override
	public <S extends T> S save(S entity) {
		// 是否需要为了检索的Bean实体
		if (entity instanceof SearchBean) {
			((SearchBean) entity).setSearchContent(entity.toString());
		}
		return super.save(entity);
	}
	@Override
	public DataFrame<Object> findDataFramesByParams(String queryName, IBaseObject params) {
		// TODO Auto-generated method stub
		return this.commonRepository.findDataFramesByParams(queryName,params);
	}
}
